package com.ibeeking.found.upms.g.rest.service.impl;

import com.ibeeking.found.common.constants.GlobalConstant;
import com.ibeeking.found.common.entity.DictDTO;
import com.ibeeking.found.common.enums.PublishStatusEnum;

import com.ibeeking.gmc.global.api.feign.GlobalDictFeignClient;
import com.ibeeking.found.upms.g.rest.service.IGmcResourceService;
import com.ibeeking.found.common.base.BaseDO;
import com.ibeeking.found.upms.service.common.bo.ResourceBO;
import com.ibeeking.found.upms.service.common.bo.ResourceTreeBO;
import com.ibeeking.found.upms.service.common.constants.RedisKeyConstant;
import com.ibeeking.found.upms.service.common.dos.ResourceDO;
import com.ibeeking.found.upms.service.common.param.ResourceParam;
import com.ibeeking.found.upms.service.common.query.ResourceQuery;
import com.ibeeking.found.upms.service.common.vo.ResourceEffectiveVO;
import com.ibeeking.found.upms.service.common.vo.ResourceTreeVO;
import com.ibeeking.found.upms.service.common.vo.ResourceVO;
import com.ibeeking.found.upms.service.mapper.ResourceMapper;
import com.ibeeking.nematos.core.utils.SpringBeanUtil;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.redis.annotation.CacheEvict;
import com.ibeeking.nematos.redis.annotation.CacheQuery;
import com.ibeeking.nematos.redis.utils.RedisUtils;
import com.ibeeking.nematos.utils.bean.BeanUtil;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.data.TreeFastUtils;
import com.ibeeking.nematos.utils.json.JsonUtils;
import com.ibeeking.nematos.utils.result.ResponseResult;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName GmcResourceServiceImpl
 * @Description
 * @Author ibeeking
 * @Date 2020-12-07 09:09
 **/
@Service
public class GmcResourceServiceImpl extends ServiceImpl<ResourceMapper, ResourceDO> implements IGmcResourceService {

    private static final String PREFIX = "/";
    private static final Integer RESOURCE_TYPE = 3;
    private static final String SERVICE_TYPE_NAME = "serviceAppType";

    @Resource
    private ResourceMapper resourceMapper;
    @Resource
    private GlobalDictFeignClient globalDictFeignClient;
    @Resource
    private RedisUtils redisUtils;

    @Override
    public List<ResourceTreeVO> queryList(ResourceQuery resourceQuery) {
        LambdaQueryWrapper<ResourceDO> queryWrapper = new LambdaQueryWrapper<>(ResourceDO.class);
        queryWrapper
                .eq(ResourceDO::getParentId, resourceQuery.getParentId());

        if (null != resourceQuery.getPublishStatus()) {
            queryWrapper.eq(ResourceDO::getPublishStatus, resourceQuery.getPublishStatus());
        }

        if (null != resourceQuery.getServiceId()) {
            queryWrapper.eq(ResourceDO::getServiceId, resourceQuery.getServiceId());
        }

        if (StringUtils.isNotBlank(resourceQuery.getName())) {
            queryWrapper.like(ResourceDO::getName, resourceQuery.getName());
        }

        if (StringUtils.isNotBlank(resourceQuery.getResource())) {
            queryWrapper.likeRight(ResourceDO::getResource, resourceQuery.getResource());
        }

        List<ResourceDO> resourceDOS = resourceMapper.selectList(queryWrapper);
        if (Collections3.isEmpty(resourceDOS)) {
            return Collections3.emptyList();
        }
        List<Long> ids = resourceDOS.stream().map(BaseDO::getId).collect(Collectors.toList());
        ids.add(resourceQuery.getParentId());
        //查询所有菜单
        List<ResourceDO> all = resourceMapper.selectList(new LambdaQueryWrapper<>(ResourceDO.class).orderByAsc(ResourceDO::getSort));
        List<ResourceTreeVO> allTrees = JsonUtils.jsonToList(all, ResourceTreeVO.class);
        all = null;
        List<Long> result = new ArrayList<>();
        TreeFastUtils.selectLongCidByPid(result, allTrees, ids);
        result.addAll(ids);
        allTrees = allTrees.stream().filter(g -> result.contains(g.getId())).collect(Collectors.toList());
        allTrees.stream().filter(resourceTreeVO -> RESOURCE_TYPE.equals(resourceTreeVO.getType())).forEach(resourceTreeVO -> {
            //查询服务名称
            ResponseResult<DictDTO> responseResult = globalDictFeignClient.queryDictLable(SERVICE_TYPE_NAME, String.valueOf(resourceTreeVO.getServiceId()));
            if (null != responseResult.getData()) {
                resourceTreeVO.setServiceName(responseResult.getData().getLabel());
            }
        });
        return TreeFastUtils.listToTree(allTrees, resourceQuery.getParentId());
    }

    @Override
    public Boolean add(ResourceParam resourceParam) {
        if (null == resourceParam.getParentId()) {
            resourceParam.setParentId(GlobalConstant.PARENT_ID);
        }
        IGmcResourceService bean = getBean();
        Long id1 = bean.checkName(resourceParam.getName(), resourceParam.getParentId());
        if (null != id1) {
            throw new BusinessException("当前级别存在同名资源名称");
        }
        if (StringUtils.isNotBlank(resourceParam.getResource()) && !resourceParam.getResource().startsWith(PREFIX)) {
            resourceParam.setResource(PREFIX + resourceParam.getResource());
        }

        if (RESOURCE_TYPE.equals(resourceParam.getType())) {
            Long id2 = bean.checkResource(resourceParam.getResource(), resourceParam.getServiceId());
            if (null != id2) {
                throw new BusinessException("当前服务下存在相同资源路径");
            }
        }

        ResourceDO resourceDO = BeanUtil.convertBean(resourceParam, ResourceDO.class);
        resourceDO.setCreateTime(LocalDateTime.now());
        resourceDO.setModifyTime(LocalDateTime.now());
        if (GlobalConstant.PARENT_ID.equals(resourceDO.getParentId())) {
            resourceDO.setLevel(1);
        } else {
            //查询父级目录级别
            ResourceBO resourceBO = bean.queryById(resourceDO.getParentId());
            resourceDO.setLevel(resourceBO.getLevel() + 1);
        }
        return resourceMapper.insert(resourceDO) > 0;
    }

    @CacheEvict(cacheName = RedisKeyConstant.MODULE_G + RedisKeyConstant.RESOURCE, key = "#resourceParam.id")
    @Override
    public Boolean modify(ResourceParam resourceParam) {
        IGmcResourceService bean = getBean();
        ResourceBO resourceBO = bean.queryById(resourceParam.getId());
        Long id1 = bean.checkName(resourceParam.getName(), resourceBO.getParentId());
        if (null != id1 && !id1.equals(resourceParam.getId())) {
            throw new BusinessException("当前级别存在同名资源名称");
        }
        if (StringUtils.isNotBlank(resourceParam.getResource()) && !resourceParam.getResource().startsWith(PREFIX)) {
            resourceParam.setResource(PREFIX + resourceParam.getResource());
        }

        if (RESOURCE_TYPE.equals(resourceBO.getType())) {
            Long id2 = bean.checkResource(resourceParam.getResource(), resourceBO.getServiceId());
            if (null != id2 && !id2.equals(resourceParam.getId())) {
                throw new BusinessException("当前服务下存在相同资源路径");
            }
        }

        ResourceDO resourceDO = BeanUtil.convertBean(resourceParam, ResourceDO.class);
        resourceDO.setParentId(null);
        resourceDO.setServiceId(null);
        resourceDO.setType(null);
        resourceDO.setModifyTime(LocalDateTime.now());
        return resourceMapper.updateById(resourceDO) > 0;
    }

    @Override
    public ResourceVO view(Long id) {
        IGmcResourceService bean = getBean();
        ResourceBO resourceBO = bean.queryById(id);
        if (null == resourceBO) {
            return null;
        }
        ResourceVO resourceVO = BeanUtil.convertBean(resourceBO, ResourceVO.class);

        //查询父级名称
        if (GlobalConstant.PARENT_ID.equals(resourceBO.getParentId())) {
            resourceVO.setParentName(GlobalConstant.PARENT_NAME);
        } else {
            ResourceBO resourceBo1 = bean.queryById(resourceBO.getParentId());
            resourceVO.setParentName(null != resourceBo1 ? resourceBo1.getName() : "");
        }
        if (RESOURCE_TYPE.equals(resourceBO.getType())) {
            //查询服务名称
            ResponseResult<DictDTO> responseResult = globalDictFeignClient.queryDictLable(SERVICE_TYPE_NAME, String.valueOf(resourceBO.getServiceId()));
            if (null != responseResult.getData()) {
                resourceVO.setServiceName(responseResult.getData().getLabel());
            }
        }
        return resourceVO;
    }

    @CacheQuery(cacheName = RedisKeyConstant.MODULE_G + RedisKeyConstant.RESOURCE, key = "#id", expires = RedisKeyConstant.MONTH)
    @Override
    public ResourceBO queryById(Long id) {
        ResourceDO resourceDO = resourceMapper.selectById(id);
        return BeanUtil.convertBean(resourceDO, ResourceBO.class);
    }

    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Boolean changeStatus(Long id, Integer status, Long userId) {
        List<ResourceDO> resourceDoS = resourceMapper.selectList(new LambdaQueryWrapper<>());
        if (Collections3.isEmpty(resourceDoS)) {
            return true;
        }
        List<Long> ids = new ArrayList<>();
        if (PublishStatusEnum.ENABLE.getValue().equals(status)) {
            //将所有父项都启用
            TreeFastUtils.selectLongPidByCid(ids, JsonUtils.jsonToList(resourceDoS, ResourceTreeBO.class), Collections.singletonList(id));
        } else {
            //将所有子项都禁用
            TreeFastUtils.selectLongCidByPid(ids, JsonUtils.jsonToList(resourceDoS, ResourceTreeBO.class), Collections.singletonList(id));
        }
        ids.add(id);
        List<ResourceDO> update = new ArrayList<>();
        List<String> cache = new ArrayList<>();
        ids.forEach(key -> {
            ResourceDO resourceDO = new ResourceDO();
            resourceDO.setId(key);
            resourceDO.setPublishStatus(status);
            resourceDO.setModifyBy(userId);
            resourceDO.setModifyTime(LocalDateTime.now());
            update.add(resourceDO);
            cache.add(RedisKeyConstant.MODULE_G + RedisKeyConstant.RESOURCE + id);
        });
        this.updateBatchById(update);
        //清除缓存
        redisUtils.del(cache);
        return true;
    }

    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Boolean del(Long id) {
        //删除所有子项
        List<ResourceDO> resourceDoS = resourceMapper.selectList(new LambdaQueryWrapper<>());
        if (Collections3.isEmpty(resourceDoS)) {
            return true;
        }
        //查询所有子项id
        List<Long> ids = new ArrayList<>();
        TreeFastUtils.selectLongCidByPid(ids, JsonUtils.jsonToList(resourceDoS, ResourceTreeBO.class), Collections.singletonList(id));
        ids.add(id);
        resourceMapper.deleteBatchIds(ids);
        List<String> cache = new ArrayList<>();
        ids.forEach(key -> {
            cache.add(RedisKeyConstant.MODULE_G + RedisKeyConstant.RESOURCE + id);
        });
        redisUtils.del(cache);
        return true;
    }

    @Override
    public Long checkName(String name, Long parentId) {
        ResourceDO resourceDO = resourceMapper.selectOne(new LambdaQueryWrapper<>(ResourceDO.class).eq(ResourceDO::getName, name).eq(ResourceDO::getParentId, parentId));
        return null != resourceDO ? resourceDO.getId() : null;
    }

    @Override
    public Long checkResource(String resource, Integer serviceId) {
        if (StringUtils.isBlank(resource) && null == serviceId) {
            return null;
        }
        ResourceDO resourceDO = resourceMapper.selectOne(new LambdaQueryWrapper<>(ResourceDO.class).eq(StringUtils.isNotBlank(resource), ResourceDO::getResource, resource).eq(null != serviceId, ResourceDO::getServiceId, serviceId));
        return null != resourceDO ? resourceDO.getId() : null;
    }

    @Override
    public List<ResourceEffectiveVO> queryEffectiveList() {
        List<ResourceDO> resourceDOS = resourceMapper.selectList(new LambdaQueryWrapper<>(ResourceDO.class)
                .eq(ResourceDO::getPublishStatus, PublishStatusEnum.ENABLE.getValue())
                .orderByAsc(ResourceDO::getSort)
                .orderByDesc(BaseDO::getCreateTime)
        );
        if (Collections3.isEmpty(resourceDOS)) {
            return Collections3.emptyList();
        }
        List<ResourceEffectiveVO> resourceEffectiveVOS = JsonUtils.jsonToList(resourceDOS, ResourceEffectiveVO.class);
        return TreeFastUtils.listToTree(resourceEffectiveVOS, GlobalConstant.PARENT_ID);
    }

    private IGmcResourceService getBean() {
        return SpringBeanUtil.getBean(IGmcResourceService.class);
    }
}
